package org.threads.circlebuffer;

/**
 * My implementation of thread-safe blocking circle buffer,
 * based on Producer-Consumer pattern.
 */
public class CircleBuffer {

    public static final String CIRCLE_BUFFER_NULL_EX = "CircleBuffer is null!";
    private static final String BUFFER_SIZE_EX = "Buffer size can't be less then 1!";

    private final int size;
    private final Object[] buffer;

    private int start = 0;
    private int end = 0;
    private int count = 0;

    /**
     * Creates CircleBuffer object with specified buffer size.
     *
     * @param bufferSize size of circle buffer
     */
    public CircleBuffer(int bufferSize) {

        if (bufferSize < 1) {
            throw new IllegalArgumentException(BUFFER_SIZE_EX);
        }

        size = bufferSize;
        buffer = new Object[bufferSize];
    }

    /**
     * Obtains an object from buffer.
     * @return object from buffer
     * @throws InterruptedException
     */
    public synchronized Object get() throws InterruptedException {

        Object get = null;

        while (count == 0) {
            wait();
        }

        get = buffer[start++];
        count--;
        if (start == size) {
            start = 0;
        }

        notifyAll();

        return get;
    }

    /**
     * Adds an object for storing in circle buffer.
     * @param obj object to store
     * @throws InterruptedException
     */
    public synchronized void add(Object obj) throws InterruptedException {

        while (count == size) {
            wait();
        }

        buffer[end++] = obj;
        count++;
        if (end == size) {
            end  = 0;
        }

        notifyAll();
    }

    /**
     * Gets the size of circle buffer.
     * @return size
     */
    public int getSize() {
        return size;
    }
}
